package cn.org.donald.framework.auth.realm;

import cn.org.donald.framework.auth.config.JWTToken;
import cn.org.donald.framework.constant.EncodeConstant;
import cn.org.donald.framework.constant.InfoConstant;
import cn.org.donald.framework.pojo.dto.LoginUser;
import cn.org.donald.framework.util.ApplicationContextUtil;
import cn.org.donald.framework.util.RequestUtil;
import cn.org.donald.pojo.entity.system.Role;
import cn.org.donald.pojo.entity.system.RolePermission;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import java.util.List;

/**
 * @author ： Donald
 * @date ： 2020/10/16 22:28
 * @description： 自定义前后端分离realm
 */
@SuppressWarnings(value = "unchecked")
public class JwtRealm extends AuthorizingRealm {

    private Logger logger = LoggerFactory.getLogger(JwtRealm.class);


    /**
     * 权限认证
     * @param principalCollection
     * @return
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info(">>>访问鉴权<<<");
        try {
            //获取主身份信息
            LoginUser loginUser = (LoginUser) principalCollection.getPrimaryPrincipal();
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

            List<Role> roleList = loginUser.getRoleList();
            List<RolePermission> rolePermissionList = loginUser.getRolePermissionList();

            roleList.forEach(role -> info.addRole(role.getRoleName()));
            if (CollectionUtils.firstElement(rolePermissionList)!=null){
                rolePermissionList.forEach(rolePermission -> info.addStringPermission(rolePermission.getPermissionName()));
            }
            return info;
        }catch (Exception e){
            throw new UnauthenticatedException(InfoConstant.FORBIDDEN);
        }
    }

    /**
     * 登录认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        logger.debug(">>>登录状态验证<<<");

        JWTToken jwtToken = (JWTToken) authenticationToken;

        String token = (String) jwtToken.getPrincipal();


        //从redis获取用户主体
        try {
            LoginUser loginUser = (LoginUser) redisTemplate().opsForValue().get(token);
            return new SimpleAuthenticationInfo(loginUser, true, this.getName());
        } catch (Exception e){
            Long userId = RequestUtil.getUserIdByAuthorization(token);
            Boolean hasKey = redisTemplate().hasKey(EncodeConstant.VERIFY_AUTHORIZATION + userId);
            if (token != null && hasKey != null && hasKey){
                Object auth = redisTemplate().opsForValue().get(EncodeConstant.VERIFY_AUTHORIZATION + userId);
                if (token.equals(auth)){
                    redisTemplate().delete(EncodeConstant.VERIFY_AUTHORIZATION + userId);
                    throw new AuthenticationException(InfoConstant.ANOTHER_DEVICE_LOGIN);
                }
            }
            throw new AuthenticationException(InfoConstant.UNAUTHORIZED);
        }
    }

    @Override
    public Class getAuthenticationTokenClass() {
        return JWTToken.class;
    }

    private static RedisTemplate redisTemplate(){
        return (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
    }
}
